import { createAsyncThunk, createSlice, isRejected } from "@reduxjs/toolkit";
import { GetParams, PageInfo, User, UserEntry, int } from "../types";
import { deleteRequest, getRequest, postRequest, putRequest } from "../util/Net";
import { RootState } from "./store";

// 1. Requests.

type GetUserRequest = GetParams & {
  name: string;
};

type UserRequest = User;

// 2. Responses.

type GetUserResponseEntry = UserEntry;

type GetUserResponse = PageInfo<GetUserResponseEntry>;

// 3. Params for reducers.

type GetUserParams = GetUserRequest;

type CreateUserParams = User;

type UpdateUserParams = UserEntry;

interface DeleteUserParams {
  uid: int;
}

// 4. State and initial value definition.

interface UserManagementState {
  entries: UserEntry[];
  loaded: boolean;
  roles: string[];
  roleLoaded: boolean;
}

const initialState: UserManagementState = {
  entries: [],
  loaded: false,
  roles: [],
  roleLoaded: false
};

// 5. Async functions definition.

const getUsers = createAsyncThunk('/user/get', async (params: GetUserParams) => {
  const response = await getRequest<GetUserResponse, GetUserRequest>({
    url: '/user',
    params: {
      page: params.page,
      pageSize: params.pageSize,
      name: params.name
    }
  });
  return response.data;
});

const createUser = createAsyncThunk('/user/post', async (params: CreateUserParams) => {
  const response = await postRequest<null, UserRequest>({
    url: '/user',
    request: {
      userName: params.userName,
      name: params.name,
      gender: params.gender,
      role: params.role
    }
  });
  return response.data;
});

const updateUser = createAsyncThunk('/user/put', async (params: UpdateUserParams) => {
  const response = await putRequest<null, UserRequest>({
    url: `/user/${params.uid}`,
    request: {
      userName: params.userName,
      name: params.name,
      gender: params.gender,
      role: params.role
    }
  });
  return response.data;
});

const deleteUser = createAsyncThunk('/user/delete', async (params: DeleteUserParams) => {
  const response = await deleteRequest({
    url: `/user/${params.uid}`
  });
  return response.data;
});

const getRoles = createAsyncThunk('/account/role/get', async () => {
  const response = await getRequest<string[]>({
    url: '/account/roles'
  });
  return response.data;
})

// 6. Slice definition.

const userManagementSlice = createSlice({
  name: 'userManagement',
  initialState: initialState,
  reducers: {
    clear: (state) => {
      state.loaded = false;
      state.entries = [];
    },
    clearRoles: (state) => {
      state.roleLoaded = false;
      state.roles = [];
    },
    refresh: (state) => {
      state.loaded = false;
    }
  },
  extraReducers: (builder) => {
    builder.addCase(getUsers.fulfilled, (state, action) => {
      const result = action.payload.result;
      state.entries = result.data;
      state.loaded = true;
    }).addCase(updateUser.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(createUser.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(deleteUser.fulfilled, (state) => {
      state.loaded = false;
    }).addCase(getRoles.fulfilled, (state, action) => {
      state.roleLoaded = true;
      state.roles = action.payload.result;
    }).addMatcher(isRejected, (state, action) => {
      console.error(action.error);
    });
  }
});

export default userManagementSlice.reducer;
export const userManagementActions = { ...userManagementSlice.actions, getUsers, updateUser, createUser, deleteUser, getRoles };
export const selectUserEntries = (root: RootState) => root.userManagementReducer.entries;
export const selectUserEntriesLoaded = (root: RootState) => root.userManagementReducer.loaded;
export const selectRoles = (root: RootState) => root.userManagementReducer.roles;
export const selectRoleLoaded = (root: RootState) => root.userManagementReducer.roleLoaded;